import {
  Viewer,
  Entity,
  SampledPositionProperty,
  ReferenceFrame,
  Color,
  TimeIntervalCollection,
  TimeInterval,
  JulianDate,
  Cartesian3,
  PolylineGlowMaterialProperty,
  ArcType,
  LagrangePolynomialApproximation,
  ClockRange,
  Timeline,
  PolylineDashMaterialProperty,
  PolylineOutlineMaterialProperty,
  CustomDataSource,
  HeightReference,
  DistanceDisplayCondition,
  CallbackProperty,
  Cartographic,
  Matrix4
} from 'cesium';

interface SatelliteOptions {
  referenceFrame?: ReferenceFrame;
  pointColor?: Color;
  pathColor?: Color;
  pointPixelSize?: number;
  pathWidth?: number;
  positions?: Position[];
  times?: TimeString[];
}

interface SatelliteRecord {
  entity: Entity;
  positionProperty: Position[];
  referenceFrame: ReferenceFrame;
  timesProperty: TimeString[],
  configProperty: SatelliteOptions
}
interface GlobalConfig {
  clock?: null | Timeline,
  times?: string[],
  DataSource: CustomDataSource | null
}

interface timeConfigType {
  startTime: Date | string,
  stopTime: Date | string,
  currentTime?: Date | string,
  shouldAnimate?: boolean,
  multiplier?: number,
  clockRange?: ClockRange
}
type GetEntityType = { entity: undefined | Entity, line: undefined | Entity };
type Position = [number, number, number]; // [x/y/lon, y/z/lat, z/alt]
type TimeString = string; // ISO 8601 format
type ReferenceFrameType = "FIXED" | "INERTIAL"
export class useSatelliteManager {
  viewer: Viewer;
  /**
   * 保存绘制的卫星对象
   */
  satellites: Map<string, SatelliteRecord>;
  _currentReferenceFrame: ReferenceFrame;
  /**
   * 属于该类的唯一标识
   */
  #niqueSign: string = "_Satellite"
  /**
   * 卫星移动对象的名称后缀
   */
  #SATELLITE_SUDDIX: string = `_PATH${this.#niqueSign}`
  /**
   * 卫星轨迹线对象的名称后缀
   */
  #LINE_SUDDIX: string = `_LINE${this.#niqueSign}`
  /**
   * 全局配置
   */
  #globalConfig: GlobalConfig = {
    DataSource: null,
    clock: null,
    times: []
  }

  /**
   * 
   */
  #viewerDataSource: CustomDataSource | Viewer;

  constructor(viewer: Viewer, config?: GlobalConfig) {

    this.viewer = viewer;
    this.satellites = new Map();
    this._currentReferenceFrame = ReferenceFrame.FIXED;

    this.#globalConfig = {
      ...this.#globalConfig,
      ...config
    }

    this.#viewerDataSource = config?.DataSource || viewer;
  }

  addSatellite(id: string, options: SatelliteOptions = {}): Entity {
    const defaults: Required<SatelliteOptions> = {
      referenceFrame: this._currentReferenceFrame,
      pointColor: Color.YELLOW,
      pathColor: Color.WHITE,
      pointPixelSize: 6,
      pathWidth: 2,
      positions: [],
      times: this.#globalConfig.times || []
    };

    const config = { ...defaults, ...options };

    const positionProperty = new SampledPositionProperty(config.referenceFrame);

    this.removeSatelliteById(id);
    const satellite = this.#viewerDataSource.entities.add({
      id: `${id}${this.#SATELLITE_SUDDIX}`,
      availability: new TimeIntervalCollection(),
      position:  positionProperty,
      // point: {
      //   pixelSize: config.pointPixelSize,
      //   color: config.pointColor,
      //   outlineColor: Color.BLACK,
      //   outlineWidth: 1
      // },
      billboard: {
        image: "/static/images/wx.png",
        scale: 1,
        width: 80,
        height: 80,
      },
      path: {
        resolution: 60,
        leadTime: 1,
        trailTime: 1,
        material: new PolylineGlowMaterialProperty({
          glowPower: 1,
          color: config.pathColor
        }),
        width: config.pathWidth
      },
      cylinder: {
        topRadius: 10,
        bottomRadius: 8000,
        length: new CallbackProperty(function (time, result) {
          const position = positionProperty.getValue(time);
          if (!position) return 1000.0; // 默认高度
          const cartographic = Cartographic.fromCartesian(position);
          let height = cartographic.height;
          return height; // 自定义高度计算
        }, false),
        fill: false,
        outline: true,  // 开启轮廓线
        outlineColor: Color.fromCssColorString("red").withAlpha(0.5),  // 轮廓线颜色
        outlineWidth: 2,  // 轮廓线宽度
        heightReference: HeightReference.CLAMP_TO_GROUND,
        // slices: 64, // 提高曲面质量
        show: true,
        material: Color.fromCssColorString("red").withAlpha(0.5)
      },
    });

    this.satellites.set(id, {
      entity: satellite,
      configProperty: config,
      positionProperty: options.positions || [],
      referenceFrame: config.referenceFrame,
      timesProperty: options.times || []
    });

    this.addSampledPositions(id, config.positions, config.times)
    return satellite;
  }

  addSampledPositions(id: string, positions: Position[], times: TimeString[]): void | boolean {
    if (!positions.length) {
      return false;
    }
    let satellite = this.satellites.get(id);

    const { entity, line } = this.getSatelliteEntity(id);

    let _times = times || this.#globalConfig.times
    let _line = line;
    // 检查时间是否合理
    if (!this._validateTimeVaildByString(_times)) {
      throw new Error('请输入合法的时间');
    }

    if (!satellite) throw new Error('Satellite not found');

    const availabilityIntervals: TimeInterval[] = [];

    let positionProperty: SampledPositionProperty | undefined = entity && (entity.position as SampledPositionProperty);

    if (!(positionProperty instanceof SampledPositionProperty)) positionProperty = new SampledPositionProperty(this._currentReferenceFrame);

    let cartesianList: Cartesian3[] = [];
    let startTime: JulianDate | null = JulianDate.fromDate(new Date(_times[0]));
    let stopTime: JulianDate | null = JulianDate.fromDate(new Date(_times[0]));
    positions.forEach((pos, index) => {
      stopTime = JulianDate.fromDate(new Date(_times[index]));

      let cartesian: Cartesian3;

      if (satellite.referenceFrame === ReferenceFrame.FIXED) {
        cartesian = Cartesian3.fromDegrees(pos[0], pos[1], pos[2]);
      } else {
        cartesian = new Cartesian3(pos[0], pos[1], pos[2]);
      }

      positionProperty.addSample(stopTime, cartesian);
      cartesianList.push(cartesian)
    });

    positionProperty.setInterpolationOptions({
      interpolationDegree: 0.0001,
      interpolationAlgorithm: LagrangePolynomialApproximation
    })
    availabilityIntervals.push(new TimeInterval({
      start: startTime,
      stop: stopTime
    }));

    if (_line) this.removeLineById(id)
    _line = this.#viewerDataSource.entities.add({
      id: `${id}${this.#LINE_SUDDIX}`,
      name: `${id}_name`,
      polyline: {
        positions: cartesianList,
        material: new PolylineGlowMaterialProperty({
          glowPower: 0.2,
          color: Color.fromCssColorString("rgba(0, 255, 255, 0.8)"),
          taperPower: 1
        }),
        width: 5,
        arcType: ArcType.GEODESIC,
        clampToGround: false
      }
    })

    satellite.entity.availability = new TimeIntervalCollection(availabilityIntervals);

    if (satellite) {
      this.satellites.set(id, {
        ...satellite,
        timesProperty: times || []
      })
    }

    if (entity) {
      entity.availability = new TimeIntervalCollection(availabilityIntervals);
      entity.position = positionProperty;
    }

  }
  /**
   * 修改坐标 地固系 - 惯性系
   * @param frame "FIXED" | "INERTIAL"
   */
  setReferenceFrame(frame: ReferenceFrameType): void {
    let _frame = frame == "FIXED" ? ReferenceFrame.FIXED : ReferenceFrame.INERTIAL;
    this._currentReferenceFrame = _frame;
    this.AgainDrawEntity({ referenceFrame: _frame })
  }
  /**
   * 设置卫星中的时间
   * @param times 
   */
  setSatelliteTimes(times: string[] | undefined) {
    if (!times || !this._validateTimeVaildByString(times)) {
      throw new Error("请提供有效时间")
    }
    this.#globalConfig.times = times;
    this.AgainDrawEntity({
      timesProperty: times
    })
  }
  /**
   * 当更改参数时，重新绘制
   */
  AgainDrawEntity(params: Partial<SatelliteRecord>) {
    let sate = this.satellites.entries();
    let sateObj = sate.next();
    while (!sateObj.done) {
      let id = sateObj.value[0];
      let obj = sateObj.value[1];
      this.addSatellite(id, {
        ...obj.configProperty,
        ...params
      })
      sateObj = sate.next();
    }
  }
  /**
   * 更新时钟
   * @param timeConfig 
   */
  _updateClockRange(timeConfig: timeConfigType): void {

    let { shouldAnimate, startTime, multiplier, currentTime, stopTime, clockRange } = {
      shouldAnimate: false,
      currentTime: null,
      multiplier: 1,
      clockRange: ClockRange.CLAMPED,
      ...timeConfig
    };

    let _clock = this.viewer.clock;

    if (!_clock) {
      throw new Error("未找到clock对象")
    }

    if (!startTime || !stopTime) {

      let _times = this.#globalConfig.times;
      if (_times && _times.length > 1) {
        _clock.startTime = JulianDate.fromDate(new Date(_times[0]));
        _clock.currentTime = JulianDate.fromDate(new Date(_times[0]));
        _clock.stopTime = JulianDate.fromDate(new Date(_times[_times.length - 1]));
      }

    } else {
      _clock.startTime = JulianDate.fromDate(new Date(startTime));
      _clock.stopTime = JulianDate.fromDate(new Date(stopTime));
      _clock.currentTime = JulianDate.fromDate(new Date(currentTime || stopTime));
    }

    _clock.clockRange = clockRange;
    _clock.shouldAnimate = shouldAnimate;
    _clock.multiplier = multiplier;

    //  * JulianDate.lessThan(start, this.viewer.clock.startTime)
    //  * JulianDate.greaterThan(stop, this.viewer.clock.stopTime)
  }
  /**
   * 通过一组字符串数组查看是否能转成合法时间
   * @param timeStr timeStr[]
   */
  _validateTimeVaildByString(timeStr: TimeString[]): Boolean {

    for (let i = 0; i < timeStr.length; i++) {
      const date = new Date(timeStr[i]);
      if (isNaN(date.getTime())) {
        console.error(`${timeStr[i]}不合法`);
        return false;
      }
    }
    return true;
  }
  /**
   * 通过id查找指定的卫星对象
   * @param id 
   * @returns 
   */
  getSatelliteEntity(id: string): GetEntityType {
    if (!id) return { entity: undefined, line: undefined };
    return {
      entity: this.viewer.entities.getById(`${id}${this.#SATELLITE_SUDDIX}`),
      line: this.viewer.entities.getById(`${id}${this.#LINE_SUDDIX}`)
    }
  }
  /**
   * 更改id删除线
   * @param id 
   * @returns 
   */
  removeLineById(id: string): boolean {
    return this.removeEntity(`${id}${this.#LINE_SUDDIX}`)
  }
  /**
   * 更改id删除移动对象
   * @param id 
   * @returns 
   */
  removeSatelliteById(id: string): boolean {
    return this.removeEntity(`${id}${this.#SATELLITE_SUDDIX}`)
  }
  /**
   * 删除对象
   * @param content 可以是id或实体对象
   * @returns boolean
   */
  removeEntity(content: string | Entity): boolean {
    if (!content) return false;
    if (content instanceof Entity) {
      return this.viewer.entities.remove(content);
    } else {
      return this.viewer.entities.removeById(content);
    }
  }
  /**
   * 根据id删除卫星对象
   * @param id 
   */
  removeSatellite(id: string): void {
    const { entity, line } = this.getSatelliteEntity(id);
    entity && this.removeEntity(entity);
    line && this.removeEntity(line)
    this.satellites.delete(id);
  }
  /**
   * 销毁实例
   */
  destroySatellite() {
    let that = this;
    let sateVal = this.satellites.keys();
    Array.from(sateVal).forEach((item: string) => {
      that.removeSatellite(item)
    })
    this.satellites.clear()
  }
}
